58 绘制饼图
58.1 引言比例的可视化
饼图(Pie Chart)是展示部分与整体关系的经典工具。在金融分析中,饼图帮助我们: - 资产配置:展示投资组合的构成 - 收入结构:主营业务与其他业务的占比 - 市场份额:不同公司的市场占有率 - 行业分布:投资的行业配置
58.2 饼图的数学基础
饼图将圆划分为若干扇形,每个扇形的角度对应一个类别的比例:
\[ \theta_i = \frac{x_i}{\sum_{j=1}^n x_j} \times 360° \]
其中: - \(x_i\):第 \(i\) 个类别的数值 - \(\theta_i\):第 \(i\) 个扇形的角度
58.3 基础饼图
平台任务1解答代码
以下代码与教学平台任务要求完全一致:
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import pandas as pd # 导入Pandas数据分析库
import numpy as np # 导入NumPy数值计算库
time=['2018年12月末','2019年3月末','2019年6月末','2019年9月末']#创建日期的列表
name=['人民币贷款','债券','委托贷款','信托贷款','股票','承兑汇票','贷款核销','外币贷款']#创建指标名称的列表
# 创建NumPy数组datas
datas=np.array([[134.69,140.98,144.71,148.58],[29.25,30.53,31.89,33.54],[12.36,12.15,11.89,11.73],[7.85,7.88,7.88,7.68],[7.01,7.06,7.13,7.24],[3.81,4.01,3.77,3.28],[3.01,3.18,3.43,3.66],
[2.21,2.18,2.21,2.19]])#创建具体数据的数组
AFRE = pd.DataFrame(data=datas,index=name,columns=time) # 创建数据框AFRE
print(AFRE) # 输出变量AFRE的值平台任务3解答代码
以下代码与教学平台任务要求完全一致:
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import matplotlib.pyplot as plt # 导入Matplotlib绑图库
import pandas as pd # 导入Pandas数据分析库
import numpy as np # 导入NumPy数值计算库
plt.rcParams["font.sans-serif"] = ["SimHei"] # 设置Matplotlib全局参数
time=['2018年12月末','2019年3月末','2019年6月末','2019年9月末']#创建日期的列表
name=['人民币贷款','债券','委托贷款','信托贷款','股票','承兑汇票','贷款核销','外币贷款']#创建指标名称的列表
# 创建NumPy数组datas
datas=np.array([[134.69,140.98,144.71,148.58],[29.25,30.53,31.89,33.54],[12.36,12.15,11.89,11.73],[7.85,7.88,7.88,7.68],[7.01,7.06,7.13,7.24],[3.81,4.01,3.77,3.28],[3.01,
3.18,3.43,3.66],[2.21,2.18,2.21,2.19]])#创建具体数据的数组
AFRE = pd.DataFrame(data=datas,index=name,columns=time) # 创建数据框AFRE
plt.figure(figsize=(11,11)) # 创建图形画布
plt.subplot(2,2,1)#第1行、第1列子图
plt.pie(x=AFRE.iloc[:,0],labels=AFRE.index,labeldistance=1.03,counterclock=False,textprops={'fontsize':12})#绘制2018年12月末各项融资存量的饼图
plt.axis('equal')#使饼图是一个圆形
plt.title(u"2018年12月末",fontsize=14) # 设置图表标题
plt.subplot(2,2,2)#第1行、第2列子图
plt.pie(x=AFRE.iloc[:,1],labels=AFRE.index,labeldistance=1.03,counterclock=False,textprops={'fontsize':12})#绘制2019年3月末各项融资存量的饼图
plt.axis('equal')#使饼图是一个圆形
plt.title(u"2019年3月末",fontsize=14) # 设置图表标题
plt.subplot(2,2,3)#第2行、第1列子图
plt.pie(x=AFRE.iloc[:,2],labels=AFRE.index,labeldistance=1.03,counterclock=False,textprops={'fontsize':12})#绘制2019年6月末各项融资存量的饼图
plt.axis('equal')#使饼图是一个圆形
plt.title(u"2019年6月末",fontsize=14) # 设置图表标题
plt.subplot(2,2,4)#第2行、第2列子图
plt.pie(x=AFRE.iloc[:,3],labels=AFRE.index,labeldistance=1.03,counterclock=False,textprops={'fontsize':12})#绘制2019年9月末各项融资存量的饼图
plt.axis('equal')#使饼图是一个圆形
plt.title(u"2019年9月末",fontsize=14) # 设置图表标题
plt.show() # 显示图形
plt.savefig("2.png") # 保存图形至文件# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import matplotlib.pyplot as plt # 导入Matplotlib绑图库
import pandas as pd # 导入Pandas数据分析库
import numpy as np # 导入NumPy数值计算库
plt.rcParams["font.sans-serif"] = ["SimHei"] # 设置Matplotlib全局参数
time=['2018年12月末','2019年3月末','2019年6月末','2019年9月末']#创建日期的列表
name=['人民币贷款','债券','委托贷款','信托贷款','股票','承兑汇票','贷款核销','外币贷款']#创建指标名称的列表
# 创建NumPy数组datas
datas=np.array([[134.69,140.98,144.71,148.58],[29.25,30.53,31.89,33.54],[12.36,12.15,11.89,11.73],[7.85,7.88,7.88,7.68],[7.01,7.06,7.13,7.24],[3.81,4.01,3.77,3.28],[3.01,
3.18,3.43,3.66],[2.21,2.18,2.21,2.19]])#创建具体数据的数组
AFRE = pd.DataFrame(data=datas,index=name,columns=time) # 创建数据框AFRE
plt.figure(figsize=(9,6)) # 创建图形画布
plt.pie(x=AFRE.iloc[:,-1],labels=AFRE.index,textprops={"fontsize":13}) #绘制2018年末各项融资存量的饼图
plt.axis("equal") #使饼图是一个图形
plt.legend(fontsize=12) # 添加图例
plt.title(u"2019年9月末各类融资存量的占比",fontsize=13) # 设置图表标题
plt.show() # 显示图形
plt.savefig("1.png") # 保存图形至文件关键参数解析:
- autopct:显示百分比的格式字符串
'%1.1f%%':保留1位小数'%1.2f%%':保留2位小数'%d%%':显示整数
- startangle:起始角度
90(或-90):从12点方向开始,符合时钟习惯0:从3点方向开始- 通常用90度更直观
- explode:突出显示
- 元素为0:正常显示
- 元素>0:向外偏移,突出该扇形
58.4 环形图
# =============================================================================
# 题目: 环形图——收入构成分析
# =============================================================================
# 本代码展示如何绘制环形图(甜甜圈图),在饼图中心添加信息
# 应用于金融场景:展示收入构成,中心显示总收入
# ==================== 收入数据 ====================
revenue_sources = ['主营业务', '其他业务', '投资收益', '资产处置'] # 收入来源
revenues = [850, 120, 80, 50] # 各项收入,单位:亿元
colors_revenue = ['#2E86AB', '#008080', '#F0A700', '#E3120B']
# 配色:蓝、青、黄、红
# ==================== 绘制环形图 ====================
fig, ax = plt.subplots(figsize=(8, 8)) # 创建画布和坐标轴对象
# 绘制饼图,然后中心挖空形成环形
wedges, texts, autotexts = ax.pie(revenues, labels=revenue_sources,
colors=colors_revenue,
autopct='%1.1f%%',
startangle=90,
pctdistance=0.85)
# pctdistance=0.85: 百分比标签距离圆心的位置(0-1之间)
# 0.85表示距离圆心85%的位置
# ==================== 创建白色圆心,形成环形 ====================
center_circle = plt.Circle((0, 0), 0.60, fc='white')
# 创建一个白色圆,半径0.60(相对于饼图半径1),覆盖在中心
# (0, 0)是圆心坐标
ax.add_artist(center_circle) # 将圆心添加到图表中
# ==================== 在中心添加总计 ====================
total_revenue = sum(revenues) # 计算总收入
ax.text(0, 0, f'总收入\n{total_revenue}亿元',
ha='center', va='center', # 水平和垂直居中
fontsize=14, fontweight='bold') # 字体大小14,加粗
# 使用\n换行,将"总收入"和数值分两行显示
# ==================== 添加标题 ====================
plt.title('收入构成', fontsize=16, fontweight='bold', pad=20)
plt.tight_layout()
plt.show()
# ==================== 收入结构分析 ====================
print('收入结构分析:') # 打印标题
for i, source in enumerate(revenue_sources): # 遍历所有收入来源
pct = revenues[i] / total_revenue * 100 # 计算百分比
print(f'{source}: {revenues[i]}亿元 ({pct:.1f}%)') # 打印每项收入和占比环形图的优势: - 更现代、美观 - 中心可放置额外信息(总计、图标等) - 减小扇形面积的视觉误导
58.5 条形图 vs 饼图
# =============================================================================
# 题目: 对比——条形图与饼图
# =============================================================================
# 本代码展示同一数据用饼图和条形图两种方式呈现,对比优缺点
# 应用于金融场景:帮助读者选择合适的图表类型
# ==================== 同一数据,两种展示方式 ====================
companies = ['公司A', '公司B', '公司C', '公司D', '公司E'] # 公司名称
market_share = [35, 25, 20, 12, 8] # 市场份额,单位:%
colors_comparison = ['#E3120B', '#008080', '#F0A700', '#2C3E50', '#8E9EAA']
# 配色方案,保持一致性
# ==================== 创建子图 ====================
fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # 1行2列的子图布局
# ==================== 左:饼图 ====================
axes[0].pie(market_share, labels=companies, colors=colors_comparison,
autopct='%1.1f%%', startangle=90) # 绘制饼图
axes[0].set_title('饼图', fontsize=14, fontweight='bold') # 子图标题
# ==================== 右:条形图 ====================
bars = axes[1].bar(companies, market_share, color=colors_comparison, alpha=0.8)
# 绘制垂直条形图,alpha=0.8设置透明度
axes[1].set_title('条形图', fontsize=14, fontweight='bold')
axes[1].set_ylabel('市场份额(%)', fontsize=12) # Y轴标签
axes[1].grid(axis='y', alpha=0.3) # 只显示Y轴网格线
# ==================== 添加数值标签 ====================
for bar in bars: # 遍历每个条形
height = bar.get_height() # 获取条形高度
axes[1].text(bar.get_x() + bar.get_width()/2., height, # 计算标签位置
f'{height}%', ha='center', va='bottom', fontsize=10) # 添加标签
# bar.get_x(): 条形左边界坐标
# bar.get_width(): 条形宽度
# ha='center': 水平居中对齐
# va='bottom': 垂直底部对齐(标签在条形上方)
plt.tight_layout() # 调整布局
plt.show() # 显示
print('使用建议:') # 打印使用建议
print('- 饼图:展示构成,适合类别较少(≤5)的情况')
print('- 条形图:精确比较数值,适合类别较多或需要精确比较')选择指南:
| 场景 | 推荐图表 | 理由 |
|---|---|---|
| 类别≤5,重点是构成 | 饼图 | 直观展示比例 |
| 类别>5 | 条形图 | 避免饼图过于细碎 |
| 需要精确比较数值 | 条形图 | 长度比角度更易比较 |
| 层次结构(饼中饼) | 环形图或嵌套饼图 | 展示多级构成 |
| 时间序列变化 | 堆叠条形图 | 饼图难以展示变化 |
58.6 嵌套饼图
# =============================================================================
# 题目: 嵌套饼图——多层级数据展示
# =============================================================================
# 本代码展示如何绘制嵌套饼图,展示两层级的层次结构
# 应用于金融场景:展示大类资产及其细分
# ==================== 两层数据 ====================
# 外层:大类资产
major_assets = ['权益类', '固定收益类', '另类投资'] # 三大类
major_alloc = [50, 35, 15] # 大类配置比例
# 内层:细分类别
sub_assets = ['A股', '港股', '国债', '信用债', '商品', 'REITs'] # 细分
sub_alloc = [30, 20, 20, 15, 8, 7] # 细分配置比例
# 确定每个子类别属于哪个大类
major_index = [0, 0, 1, 1, 2, 2] # 对应major_assets的索引
# A股、港股属于权益类(索引0)
# 国债、信用债属于固定收益类(索引1)
# 商品、REITs属于另类投资(索引2)
# ==================== 绘制嵌套饼图 ====================
fig, ax = plt.subplots(figsize=(10, 10)) # 创建10×10英寸画布
# ==================== 内层饼图 ====================
wedges_inner, texts_inner, autotexts_inner = ax.pie(
sub_alloc,
radius=0.7, # 内层半径0.7
labels=sub_assets, # 标签
colors=['#E3120B', '#F0656E', '#008080', '#46A1A8', '#F0A700', '#F7C555'],
# 使用渐变色,同一大类用相近颜色
autopct='%1.1f%%',
startangle=90,
pctdistance=0.85, # 百分比标签距离
labeldistance=0.6 # 类别标签距离
)
# ==================== 外层饼图 ====================
wedges_outer, texts_outer, autotexts_outer = ax.pie(
major_alloc,
radius=1.0, # 外层半径1.0
labels=major_assets,
colors=['#E3120B', '#008080', '#F0A700'], # 大类颜色
autopct='%1.1f%%', # 显示百分比(需返回3个值)
startangle=90,
labeldistance=1.05, # 标签距离略大于1,避免与内层重叠
textprops={'fontsize': 12, 'fontweight': 'bold'} # 标签字体加粗
)
# ==================== 添加标题 ====================
plt.title('投资组合多层级构成', fontsize=16, fontweight='bold', pad=20)
plt.tight_layout()
plt.show()
# ==================== 打印层级结构 ====================
print('投资组合层级结构:') # 打印标题
for i, major in enumerate(major_assets): # 遍历每个大类
sub_indices = [j for j, x in enumerate(major_index) if x == i]
# 找出属于该大类的所有子类别的索引
print(f'\n{major} ({major_alloc[i]}%):') # 打印大类名称和比例
for idx in sub_indices: # 遍历该大类的子类别
print(f' - {sub_assets[idx]}: {sub_alloc[idx]}%') # 打印子类别
# 缩进两个空格,表示层级关系58.7 金融应用行业配置
# =============================================================================
# 题目: 基金行业配置分析
# =============================================================================
# 本代码展示如何对比基金与基准的行业配置差异
# 应用于金融场景:基金投资组合的行业配置偏离度分析
# ==================== 行业配置数据 ====================
industries = ['金融', '科技', '消费', '医疗', '新能源', '原材料', '其他'] # 行业列表
fund_allocation = [25, 20, 18, 12, 10, 8, 7] # 基金配置比例
benchmark_allocation = [22, 15, 20, 10, 12, 10, 11] # 基准(如沪深300)配置比例
# ==================== 创建子图 ====================
fig, axes = plt.subplots(1, 2, figsize=(14, 7)) # 1行2列
# ==================== 左:基金配置(饼图) ====================
axes[0].pie(fund_allocation, labels=industries,
autopct='%1.1f%%', startangle=90,
colors=['#2E86AB', '#008080', '#E3120B', '#F0A700',
'#2C3E50', '#8E9EAA', '#A8B5BF'])
axes[0].set_title('基金行业配置', fontsize=14, fontweight='bold')
# ==================== 右:与基准对比(条形图) ====================
x = np.arange(len(industries)) # 生成0-6的整数序列,作为X轴位置
width = 0.35 # 条形宽度0.35
# 绘制两组条形图,并排显示
axes[1].bar(x - width/2, fund_allocation, width,
label='基金', color='#2E86AB', alpha=0.8)
# x - width/2: 左移半个条形宽度,居中显示
axes[1].bar(x + width/2, benchmark_allocation, width,
label='基准', color='#E3120B', alpha=0.8)
# x + width/2: 右移半个条形宽度
# ==================== 添加坐标轴标签和标题 ====================
axes[1].set_xlabel('行业', fontsize=12)
axes[1].set_ylabel('配置比例(%)', fontsize=12)
axes[1].set_title('vs 基准对比', fontsize=14, fontweight='bold')
axes[1].set_xticks(x) # 设置X轴刻度位置
axes[1].set_xticklabels(industries, rotation=45) # 设置刻度标签,旋转45度避免重叠
axes[1].legend(fontsize=11) # 显示图例
axes[1].grid(axis='y', alpha=0.3) # Y轴网格线
plt.tight_layout()
plt.show()
# ==================== 计算偏离度 ====================
deviation = [f - b for f, b in zip(fund_allocation, benchmark_allocation)]
# 计算基金与基准的差值
df_deviation = pd.DataFrame({
'行业': industries,
'基金': fund_allocation,
'基准': benchmark_allocation,
'偏离度': deviation # 正值表示超配,负值表示低配
})
print('\n配置偏离分析:') # 打印标题
print(df_deviation) # 打印偏离度表格
# ==================== 分析超配和低配 ====================
print(f'\n超配: {[industries[i] for i, d in enumerate(deviation) if d > 0]}')
# 列出所有超配行业(偏离度>0)
print(f'低配: {[industries[i] for i, d in enumerate(deviation) if d < 0]}')
# 列出所有低配行业(偏离度<0)58.8 饼图的注意事项
何时使用饼图: ✅ 适合: - 展示部分与整体的关系 - 类别较少(≤6个) - 各部分比例差异明显 - 重点在于构成,非精确比较
❌ 不适合: - 类别过多(>8个) - 需要精确比较数值 - 各部分比例相近(难以区分) - 需要展示时间序列变化
最佳实践:
- 排序:按大小顺时针排列,从12点开始
- 突出关键:用explode或颜色突出重点
- 颜色:使用对比色,避免彩虹色
- 标签:直接标注,避免图例来回对照
- 总数:在标题或中心说明总数值